#pragma once
#include <atomic>
#include <functional>

template <class T>
class SmartPtr
{
public:
	// RAII
	SmartPtr(T* ptr)
		:_ptr(ptr)
	{}

	~SmartPtr()
	{
		cout << "~SmartPtr()->" << _ptr << endl;
		delete _ptr;
	}

	// ��ָ��һ��
	T& operator*()
	{
		return *_ptr;
	}

	T* operator->()
	{
		return _ptr;
	}

private:
	T* _ptr;
};

namespace lcx
{
	// 智能指针总结：
	// C++98 auto_ptr 管理权限转移->不好的设计，对象悬空
	// C++11 unique_ptr 防拷贝->简单粗暴，对于不需要拷贝的场景非常好
	// C++11 shared_ptr 引用计数，最后一个释放的对象释放资源->复杂一些，但是支持拷贝
	// 非常完美，-> 问题：循环引用
	// weak_ptr 解决了shared_ptr的问题，更优
	// 下面我们一一来学习

	// C++98
	// 管理权转移，最后一个拷贝对象管理资源，被拷贝对象都被置空
	template <class T>
	class auto_ptr
	{
	public:
		auto_ptr(T* ptr)
			:_ptr(ptr)
		{}

		~auto_ptr()
		{
			if (_ptr) // ��Ϊ��������
			{
				cout << "~auto_ptr()" << endl;
				delete _ptr;
				_ptr = nullptr;
			}
		}

		// 这里实现RAII是管理权转移
		// ap2(ap1)
		auto_ptr(auto_ptr<T>& ap)
			:_ptr(ap._ptr)
		{
			ap._ptr = nullptr;
		}

		// 像指针一样
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}

	private:
		T* _ptr;
	};


	// C++11
	template <class T>
	class unique_ptr
	{
	public:
		// RAII
		unique_ptr(T* ptr)
			:_ptr(ptr)
		{}

		~unique_ptr()
		{
			cout << "~unique_ptr()->" << _ptr << endl;
			delete _ptr;
		}

		// 像指针一样
		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}

		// C++11
		unique_ptr(const unique_ptr<T>& up) = delete;
		unique_ptr<T> operator=(const unique_ptr<T>& up) = delete;
	private:
		// C++98
		// 1. 只声明，不实现
		// 问题：可能有人会在类外面实现。第二点解决
		// 2. 限定为私有
		// 还得限制赋值，防拷贝
		// unique_ptr(const unique_ptr<T>& up);
		// unique_ptr<T> operator=(const unique_ptr<T>& up);

	private:
		T* _ptr;
	};


	// 引用计数版本（最有可能手撕）
	// 计数代表有多少个对象在管理这份资源
	template <class T>
	class shared_ptr
	{
	public:
		// RAII
		shared_ptr(T* ptr = nullptr)
			:_ptr(ptr)
			, _pcount(new int(1))
		{}

		template <class D>
		shared_ptr(T* ptr, D del)
			:_ptr(ptr)
			, _pcount(new int(1))
			, _del(del)
		{}

		~shared_ptr()
		{
			release();
		}

		shared_ptr(const shared_ptr<T>& sp)
			:_ptr(sp._ptr)
			, _pcount(sp._pcount) // 浅拷贝
		{
			(*_pcount)++;
		}

		void release()
		{
			if (--(*_pcount) == 0)
			{
				//delete _ptr;
				_del(_ptr);
				delete _pcount;
			}
		}

		shared_ptr<T>& operator=(const shared_ptr<T>& sp)
		{
			// if(this != &sp) // 防不住sp2(sp1),再sp1 = sp2
			if (_ptr != sp._ptr)
			{
				// 赋值前考虑, 被赋值的对象内计数需要--操作, --操作后,
				// 计数为 0 就需要先释放掉, 再将值赋给被赋值对象
				release();

				_ptr = sp._ptr;
				_pcount = sp._pcount;
				(*_pcount)++;
			}
			return *this;
		}

		// 像指针一样
		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}

		int use_count() const
		{
			return *_pcount;
		}

		T* get() const
		{
			return _ptr;
		}
	private:
		T* _ptr;
		//int _count; // 这样不可以，彼此不同步，各个类都有一份
		//static int _count; // 不去拷贝也是一份，会相互影响
		// int* _pcount; // 每次到堆上开，拷贝时用一份，非拷贝各自用各自的, 存在线程安全问题
		std::atomic<int>* _pcount;

		std::function<void(T*)> _del = [](T* ptr) { delete ptr; };
	};


	// weak_ptr 不支持RAII
	template <class T>
	class weak_ptr
	{
	public:
		weak_ptr()
			:_ptr(nullptr)
		{}

		weak_ptr(const shared_ptr<T>& sp)
			:_ptr(sp.get())
		{}

		weak_ptr& operator=(const shared_ptr<T>& sp)
		{
			_ptr = sp.get();
			return *this;
		}

		// 像指针一样
		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}
	private:
		T* _ptr;
	};
};